package me.flyray.bsin.gateway.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import me.flyray.bsin.context.LoginInfoContextHelper;
import me.flyray.bsin.exception.BusinessException;
import me.flyray.bsin.gateway.config.SecurityProperties;
import me.flyray.bsin.gateway.utils.HttpHelper;

/**
 * @author ：bolei
 * @date ：Created in 2021/12/17 13:02
 * @description：
 * @modified By：
 */
@Slf4j
public class TokenValidationFilter implements Filter {

    private static final SecurityProperties SECURITY_PROPERTIES;

    static {
        SECURITY_PROPERTIES = SpringUtil.getBean(SecurityProperties.class);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException, BusinessException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
        //获取用户凭证
        //=================获取json格式的token字段=========================
        String body = HttpHelper.getBodyString(requestWrapper);
        log.info("请求参数-------------------{}", body);
        // 此处是一个大坑，浏览器发起的一个请求上来，会进去两次，第一次body数据为空，只需要放过，postman等工具发起的只会进入一次
        if (StringUtils.isNotBlank(body)) {
            log.info("请求参数-------------------{}", body);
            JSONObject bodyData = JSONObject.parseObject(body);
            String serviceName = String.valueOf(bodyData.get("serviceName"));
            String serviceMethod = String.valueOf(bodyData.get("methodName"));
            // 校验token
            String token = httpServletRequest.getHeader("Authorization");

            Set<String> serviceWhitelist = SECURITY_PROPERTIES.getServiceWhiteList();
            Set<String> methodWhitelist = SECURITY_PROPERTIES.getMethodWhiteList();
            Map<String, String[]> map = SECURITY_PROPERTIES.getServiceMethodList().stream()
                    .map(v -> v.split(":"))
                    .collect(Collectors.toMap(v -> v[0] + ":" + v[1], Function.identity(), (key1, key2) -> key2));

            boolean existWhitelist =  map.containsKey(String.format("%s:%s", serviceName, serviceMethod))
                    || serviceWhitelist.contains(serviceName)
                    || methodWhitelist.contains(serviceMethod);
            boolean existToken = StringUtils.isNotBlank(token);
            if(existToken){
                existToken = !token.equals("null");
            }
            // 在白名单但存在token优先解析token
            if(existWhitelist && !existToken){
                chain.doFilter(requestWrapper, response);
                return;
            }
            if (!existToken) {
                // 将异常分发到/error/exthrow控制器
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
                request.getRequestDispatcher("/exthrow").forward(request, httpServletResponse);
                return;
            }
            // 解析token    校验token
            JWT jwt = null;
            try {
                jwt = JWTUtil.parseToken(token);
                requestWrapper.setAttribute("token", token);
            } catch (Exception e) {
                System.out.println(e);
                // 将异常分发到/error/exthrow控制器
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
                request.getRequestDispatcher("/exthrow").forward(request, httpServletResponse);
                return;
            }
            //将jwt里存储的数据全部注入到 LoginInfoContextHelper里
            cn.hutool.json.JSONObject payloadJson = jwt.getPayloads();
            JSONObject loginJson = JSON.parseObject(payloadJson.toString());
            loginJson.put("token",token);
            LoginInfoContextHelper.setLoginUser(loginJson);
        }
//        else {
//            // 将异常分发到/error/exthrow控制器
//            request.getRequestDispatcher("/error/exthrow").forward(request,response);
//            return;
//        }
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {

    }

    public String createToken(Map<String, Object> payload) {
        DateTime now = DateTime.now();
        DateTime newTime = now.offsetNew(DateField.MINUTE, 1);
        //签发时间
        payload.put(JWTPayload.ISSUED_AT, now);
        //过期时间
        payload.put(JWTPayload.EXPIRES_AT, newTime);
        //生效时间
        payload.put(JWTPayload.NOT_BEFORE, now);
        String token = JWTUtil.createToken(payload, "1234".getBytes());
        return token;
    }


    public static void main(String[] args) {

        Map<String, Object> map = new HashMap<String, Object>();
        DateTime now = DateTime.now();
        DateTime newTime = now.offsetNew(DateField.MINUTE, 1);
        //签发时间
        map.put(JWTPayload.ISSUED_AT, now);
        //过期时间
        map.put(JWTPayload.EXPIRES_AT, newTime);
        //生效时间
        map.put(JWTPayload.NOT_BEFORE, now);
        String token = JWTUtil.createToken(map, "1234".getBytes());
        System.out.println(token);
        JWT jwt = JWTUtil.parseToken(token);
        System.out.println(jwt);


    }
}
